//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
/// @file	psys_container.h
///
///
///
//==================================================================

#ifndef PSYS_CONTAINER_H
#define PSYS_CONTAINER_H

#include <memory.h>
#include <new>
#include <vector>
#include <tchar.h>
#include "psys_types.h"

//==================================================================
//== Templates !!!
//==================================================================
template <class T>
T *ArrayAllocator( T * &io_ptr, size_t &io_n_used, size_t &io_n_alloc, size_t n_new_elems ) throw(...)
{
	size_t	req_alloc_n = io_n_used + n_new_elems;

	if ( req_alloc_n > io_n_alloc )
	{
		if ( (io_n_alloc*2) > req_alloc_n )
			req_alloc_n = io_n_alloc*2;

		// alloc new
		T	*new_p = (T *)(new char[ req_alloc_n * sizeof(T) ]);

		// copy the data to the new place
		memcpy( new_p, io_ptr, sizeof(T)*io_n_alloc );

		// delete the old data block as char !!
		delete [] (char *)io_ptr;

		// update the pointer to the new data
		io_ptr = new_p;
		// update the allocated quantity variable
		io_n_alloc = req_alloc_n;
	}

	io_n_used += n_new_elems;

	// call the constructors for the new objects through a "placement new"
	for (size_t i=io_n_used-n_new_elems; i < io_n_used; ++i)
	{
		new (io_ptr + i) T;
	}

	// return the pointer to the first of the new elements
	return io_ptr + (io_n_used-n_new_elems);
}

//==================================================================
template <class T>
T *NewAlign64( size_t n_elems ) throw(...)
{
	PALIGN(64) struct new_align_64_struct 
	{
		u_int	a0;
	};

	new_align_64_struct	*ptr1 = new new_align_64_struct[ (n_elems * sizeof(T) + sizeof(new_align_64_struct)-1) / sizeof(new_align_64_struct) ];

	new ( ptr1 ) T();

	return (T *)ptr1;
}

//==================================================================
template <typename T>
class PArray
{
public:
	T		*_p;
	size_t	_n;
	size_t	_data_n_alloc;

	//==================================================================
	typedef			T	*iterator;
	typedef const	T	*const_iterator;

	iterator		begin()			{ return _p;					}
	const_iterator	begin() const	{ return (const T *)_p;			}
	iterator		end()			{ return _p + _n;				}
	const_iterator	end() const		{ return (const T *)(_p + _n);	}

	T				& front()		{ return _p[0];	}
	const T			& front() const	{ return _p[0];	}

	T				& back()		{ return _p[_n-1];	}
	const T			& back() const	{ return _p[_n-1];	}

	//==================================================================
	PArray( size_t suggested_init_n=0 ) :
		_p(0),
		_n(0),
		_data_n_alloc(0)
	{
		if ( suggested_init_n )
			ArrayAllocator<T>( _p, _n, _data_n_alloc, suggested_init_n );
	}

	//==================================================================
	~PArray()
	{
		clear();
		delete [] _p;
	}
	//==================================================================
	size_t len() const
	{
		return _n;
	}
	//==================================================================
	size_t size_bytes() const
	{
		return _n * sizeof(T);
	}
	//==================================================================
	void erase(size_t index)
	{
		PASSERT( index >= 0 && index < _n );
		_p[index].~T();
		_n -= 1;
		for (size_t i=index; i < _n; ++i)
			_p[i] = _p[i+1];
	}
	//==================================================================
	void erase( T *ptr )
	{
		erase( ptr - _p );
	}
	//==================================================================
	void pop_back()
	{
		if ( _n )
		{
			_p[--_n].~T();
		}
	}
	//==================================================================
	void clear()
	{
		// delete all objects in use
		for (size_t i=0; i < _n; ++i)
			_p[i].~T();

		_n = 0;
	}
	//==================================================================
	void delete_ptrs()
	{
		// delete all objects in use
		for (size_t i=0; i < _n; ++i)
			delete _p[i];
	}
	//==================================================================
	void hint_expand( size_t expand_n )
	{
		size_t	cur_n;
		expand( expand_n );
		_n = cur_n;
	}

	//==================================================================
	T *expand( size_t add_n=1 )
	{
		return ArrayAllocator<T>( _p, _n, _data_n_alloc, add_n );
	}

	//==================================================================
	void resize( size_t new_n )
	{
		if ( new_n > _n )
		{
			expand( new_n - _n );
		}
		else
		if ( new_n < _n )
		{
			// destroy the elements in excess
			for (size_t i=new_n; i < _n; ++i)
				_p[i].~T();

			// assign the new value
			_n = new_n;
		}
	}
	//==================================================================
	void push_back( const T &value )
	{
		*expand() = value;
	}

	//==================================================================
	const T &operator[] (size_t index) const
	{
		PASSERT( index >= 0 && index < _n );
		return _p[ index ];
	}
	//==================================================================
	T &operator[] (size_t index)
	{
		PASSERT( index >= 0 && index < _n );
		return _p[ index ];
	}
	//==================================================================
	void fill( const T &val )
	{
		T	*p		= _p;
		T	*endp	= _p + _n;

		while ( p < endp )
		{
			*p++ = val;
		}
	}
	//==================================================================
	void clone( const PArray<T> &from )
	{
		resize( from.len() );
		memcpy( _p, from._p, from.size_bytes() );
	}

};

//==================================================================
template <typename T>
class PArraySta
{
public:
	T		*_p;
	size_t	_n;
	size_t	_data_n_alloc;
	bool	_is_read_only;

	//==================================================================
	typedef			T	*iterator;
	typedef const	T	*const_iterator;

	iterator		begin()			{ return _p;					}
	const_iterator	begin() const	{ return (const T *)_p;			}
	iterator		end()			{ return _p + _n;				}
	const_iterator	end() const		{ return (const T *)(_p + _n);	}

	T				& front()		{ return _p[0];	}
	const T			& front() const	{ return _p[0];	}

	T				& back()		{ return _p[_n-1];	}
	const T			& back() const	{ return _p[_n-1];	}

	//==================================================================
	PArraySta() :
		_p(NULL),
		_n(0),
		_data_n_alloc(0),
		_is_read_only(true)
	{
	}
	//==================================================================
	PArraySta( void *src_datap, size_t data_n_alloc ) :
		_p(src_datap),
		_n(0),
		_data_n_alloc(data_n_alloc),
		_is_read_only(false)
	{
	}
	//==================================================================
	PArraySta( const void *src_datap, size_t data_n_alloc ) :
		_p(src_datap),
		_n(0),
		_data_n_alloc(data_n_alloc),
		_is_read_only(true)
	{
	}

	//==================================================================
	void Setup( T *src_datap, size_t data_n, size_t data_n_alloc )
	{
		_p = src_datap;
		_n = data_n;
		_data_n_alloc = data_n_alloc;
		_is_read_only = false;
	}

	//==================================================================
	void Setup( const T *src_datap, size_t data_n, size_t data_n_alloc )
	{
		_p = (T *)src_datap;
		_n = data_n;
		_data_n_alloc = data_n_alloc;
		_is_read_only = true;
	}

	//==================================================================
	~PArraySta()
	{
		if NOT( _is_read_only )
			clear();
	}
	//==================================================================
	size_t len() const
	{
		return _n;
	}
	//==================================================================
	size_t size_bytes() const
	{
		return _n * sizeof(T);
	}
	//==================================================================
	void erase(size_t index)
	{
		PASSERT( !_is_read_only );
		PASSERT( index >= 0 && index < _n );
		_p[index].~T();

		_n -= 1;
		for (size_t i=index; i < _n; ++i)
			_p[i] = _p[i+1];
	}
	//==================================================================
	void erase( T *ptr )
	{
		erase( ptr - _p );
	}
	//==================================================================
	void pop_back()
	{
		PASSERT( !_is_read_only );
		if ( _n )
		{
			_p[--_n].~T();
		}
	}
	//==================================================================
	void clear()
	{
		PASSERT( !_is_read_only );
		// delete all objects in use
		for (size_t i=0; i < _n; ++i)
			_p[i].~T();

		_n = 0;
	}
	//==================================================================
	void delete_ptrs()
	{
		PASSERT( !_is_read_only );
		
		// delete all objects in use
		for (size_t i=0; i < _n; ++i)
			delete _p[i];
	}
	//==================================================================
	void hint_expand( size_t expand_n )
	{
		PASSERT( !_is_read_only );
		size_t	cur_n;
		expand( expand_n );
		_n = cur_n;
	}

	//==================================================================
	T *expand( size_t add_n=1 )
	{
		PASSERT( !_is_read_only );
		if ( _n + add_n > _data_n_alloc )
			throw "Out of bounds of static data !";

		T	*retp = _p + _n;

		_n += add_n;

		return retp;
	}

	//==================================================================
	void resize( size_t new_n )
	{
		PASSERT( !_is_read_only );
		
		if ( new_n > _n )
		{
			expand( new_n - _n );
		}
		else
		if ( new_n < _n )
		{
			// destroy the elements in excess
			for (size_t i=new_n; i < _n; ++i)
				_p[i].~T();

			// assign the new value
			_n = new_n;
		}
	}
	//==================================================================
	void push_back( const T &value )
	{
		PASSERT( !_is_read_only );
		*expand() = value;
	}

	//==================================================================
	const T &operator[] (size_t index) const
	{
		PASSERT( index >= 0 && index < _n );
		return _p[ index ];
	}
	//==================================================================
	T &operator[] (size_t index)
	{
		PASSERT( !_is_read_only );
		PASSERT( index >= 0 && index < _n );
		return _p[ index ];
	}
	//==================================================================
	void fill( const T &val )
	{
		PASSERT( !_is_read_only );
		T	*p		= _p;
		T	*endp	= _p + _n;

		while ( p < endp )
		{
			*p++ = val;
		}
	}
	//==================================================================
	void clone( const PArray<T> &from )
	{
		PASSERT( !_is_read_only );
		resize( from.len() );
		memcpy( _p, from._p, from.size_bytes() );
	}

};

//==================================================================
///
//==================================================================
class BitMap
{
	u_char	*_mapp;
	int		_n;

public:
	BitMap( u_char *mapp, int n ) :
		_mapp(mapp),
		_n(n)
	{
	}
	BitMap() :
		_mapp(0),
		_n(0)
	{
	}

	void Setup( u_char *mapp, int n )
	{
		_mapp = mapp;
		_n = n;
	}

	void Setup( const u_char *mapp, int n )
	{
		_mapp = (u_char *)mapp;
		_n = n;
	}

	void Setup( PArray<u_char> &themap )
	{
		_mapp = themap.begin();
		_n = themap.len() * 8;
	}

	void Setup( const PArray<u_char> &themap )
	{
		_mapp = (u_char *)themap.begin();
		_n = themap.len() * 8;
	}

	void Reset()
	{
		memset( _mapp, 0, (size_t)((_n+7)/8) );
	}

	void Fill()
	{
		memset( _mapp, 0xff, (size_t)((_n+7)/8) );
	}

	void SetOn( int idx )
	{
		_mapp[idx/8] |= 1 << (idx & 7);
	}

	void SetOff( int idx )
	{
		_mapp[idx/8] &= ~(1 << (idx & 7));
	}


	int Check( int idx ) const
	{
		return _mapp[idx/8] & (1 << (idx & 7));
	}

	const u_char	*GetMapPtr() const
	{
		return _mapp;
	}
	u_char			*GetMapPtr()
	{
		return _mapp;
	}

	u_int	GetN() const
	{
		return _n;
	}
};

#endif

